# __author__ = "James Zhang"
# !/usr/bin/python
# -*- coding: utf-8 -*-
import json

import redis
from redis.exceptions import WatchError
from django.conf import settings

from functools import wraps

HEAD_SEQ = settings.REDIS_HEAD_SEQ
CONNECT_SEQ = settings.REDIS_CONNECT_SEQ
DEFAULT_ENCODING = settings.ENCODING
CACHE_TIME = settings.REDIS_CACHE_TIME


class RedisConfig:
    HOST = settings.REDIS_HOST
    PORT = settings.REDIS_PORT
    PASSWORD = settings.REDIS_PASSWORD
    # DB = settings.REDIS_DB


class RedisCache(object):
    def __init__(self):
        if not hasattr(RedisCache, 'pool'):
            try:
                RedisCache.create_pool()
            except:         # 假设第一个Redis服务器无法连接，自动连接第二个备用的Redis服务器
                RedisConfig.HOST = settings.REDIS_BK_HOST
                RedisCache.create_pool()
        self._connection = redis.Redis(connection_pool=RedisCache.pool)
        self.pipe = self._connection.pipeline()

    @staticmethod
    def create_pool():
        RedisCache.pool = redis.ConnectionPool(
            host=RedisConfig.HOST,
            port=RedisConfig.PORT,
            # db=RedisConfig.DB,
            password=RedisConfig.PASSWORD
        )

    def set(self, key, value, expire=None):
        """
            设置键值,并设置存活时间,若expire未提供或为None,则该键永远存活.

            :param key: 键名
            :param value: 值
            :param expire: 存活时长(整数值), 默认值为None,即永远

            ...> r.set('user:test', {'name': 'test'})

            注意,在Redis中,数字型数据存储到Redis中后会被转换成字符串.

            建议:
                将需要存储的数据先用json序列化,然后再存储到Redis中,取出时再进行反序列化.

        """
        while 1:
            try:
                value = json.dumps(value)
                self.pipe.watch(key)
                self.pipe.set(key, value, expire)
                self.pipe.execute()
                break
            except WatchError:
                continue
            finally:
                self.pipe.reset()

    def get(self, key):
        """
            获取key的值

            :param key:
            ...> r.set("users:test", {'name':'test', 'email': 'test@foxconn.com'}, 3000)
            ...> r.get("users:test")
                {'name':'test', 'email': 'test@foxconn.com'}
        """
        value = self._connection.get(key)
        if not value:
            return value
        return json.loads(value.decode(settings.DEFAULT_CHARSET))

    def delete(self, key):
        """
            删除一个键

            :param key:

        """
        return self._connection.delete(key)

    def hset(self, set_name, key, value):
        """
            当有大量类型文档的对象,文档的内容都不一样时(即"表"没有固定的列),可以使用hash:

            :param set_name: 集合名称
            :param key: 键名
            :param value: 值

            ...> r.hset('users:james', 'name', 'James Zhang')
            ...> r.hset("users:james", 'email', 'james.xj.zhang@foxconn.com')
            ...> r.hset("users:james", 'ext-phone', '560187824')
            ...> r.hincrby("users:james", "visits", 1)
            ...> r.hgetall("users:james")
                {
                    'ext-phone': '560187824',
                    'name': 'James Zhang',
                    "visits": 1,
                    'email': 'james.xj.zhang@foxconn.com'
                }
            ...> r.hkeys("users:james")
                ['name', 'email', 'phone', 'visits']
        """
        return self._connection.hset(set_name, key, value)

    def hget(self, set_name, key):
        """
            返回集合中某个键的值,若键不存在,则返回None

            :param set_name: 集合名称
            :param key: 键名

            ...> r.hget("users:james", 'name')
                'James Zhang'
        """
        return self._connection.hget(set_name, key)

    def hget_keys(self, set_name):
        """
            返回集合内所有key名称的列表.

            :param set_name:

            ...> r.hkeys("users:james")
                ['name', 'email', 'phone', 'visits']
        """
        return self._connection.hkeys(set_name)

    def hget_all(self, set_name):
        """
            获取集合的全部内容

            :param set_name: 集合名称

            ...> r.hgetall("users:james")
                {
                    'ext-phone': '560187824',
                    'name': 'James Zhang',
                    "visits": 1,
                    'email': 'james.xj.zhang@foxconn.com'
                }
        """
        return self._connection.hgetall(set_name)

    def hincrby(self, set_name, key, value=1):
        return self._connection.hincrby(set_name, key, value)

    def hincrbyfloat(self, set_name, key, value=1.0):
        return self._connection.hincrby(set_name, key, value)

    def keys(self, pattern="*"):
        """
            获取一类keys,若未提供pattern,则返回当前数据库的所有keys.

            :param pattern:
        """
        return self._connection.keys(pattern)

    def dbsize(self):
        """
            当前数据库中包含多少条数据

        """
        return self._connection.dbsize()

    def flushdb(self):
        """
            清空当前数据库的所有内容

        """
        return self._connection.flushdb()

    def exists(self, key):
        """
            检查key是否存在

            :param key:
        """
        return self._connection.exists(key)


def get_redis_client():
    """
        返回一个redis客户端
    """
    return RedisCache()


def write_or_read_json_from_cache(key_suffix, auto_add_username=True, expire=None, encoding=DEFAULT_ENCODING):
    """
        缓存或读取数据json格式的数据

        若有缓存的数据,将缓存数据读取,若无,则从数据库从读取.
        若连接Redis服务器失败,则发送邮件给网站管理员
        :param encoding:
        :param expire:
        :param auto_add_username:  自动添加用户名为前缀
        :param key_suffix: 若auto_add_username=False,则key=key_suffix,否则key="ST-" + request.user.username + key_suffix

    """
    try:
        redis_client = get_redis_client()
    except:
        # ----------------------------------------
        # 需要添加Redis服务器出错后的处理动作的代码
        # ----------------------------------------
        redis_client = None

    def wrapper(func):
        @wraps(func)
        def content(request, *args, **kwargs):

            if not settings.REDIS_ON:   # 假设我要关掉Redis,设置为False即可
                return func(request, *args, **kwargs)

            if auto_add_username:
                key = HEAD_SEQ + request.user.username + key_suffix
            else:
                key = HEAD_SEQ + key_suffix
            if redis_client:
                res = redis_client.get(key)
            else:
                res = None
            if not res:
                res = func(request, *args, **kwargs)
                # json_data = json.dumps(res)
                # redis_client.set(key, json_data, expire)
                redis_client.set(key, res, expire)
            # else:
            #     res = res.decode(encoding)
            #     res = json.loads(res)
            return res
        return content
    return wrapper


def write_or_read_func_cache(only_key, key_suffix, expire=CACHE_TIME):
    """
    适用于取值时间比较长，使用频率比较高的函数
    :param key_suffix:  需要被存储的参数的名字，如用户的权限
    :param only_key:    尽量使用唯一键名，如工号等
    :param expire:      如果参数是空，则取设置中的默认时间
    # :param encoding:    编码方式
    :return:
    """

    try:
        redis_client = get_redis_client()
    except:
        redis_client = None

    def wrapper(func):
        @wraps(func)
        def content(*args, **kwargs):

            if not settings.REDIS_ON:   # 假设我要关掉Redis,设置为False即可
                return func(*args, **kwargs)

            key = HEAD_SEQ + only_key + CONNECT_SEQ + key_suffix

            data = redis_client.get(key)
            if data:
                return data
            else:
                data = func(*args, **kwargs)
                redis_client.set(key, data, expire)
                return data
        return content
    return wrapper


# def clear
